namespace SimpleWebServices.Globalization
{
    using System;
    using System.Globalization;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
    using System.Text.RegularExpressions;

    [Serializable]
    public sealed class Language : IComparable, IComparable<Language>, IEquatable<Language>, ISerializable
    {
        private static readonly Regex _regex = new Regex(@"^[a-zA-Z]{2}(?:-[a-zA-Z]{2})?$");
        private string _value;

        public Language()
            : this(CultureInfo.InvariantCulture.Name)
        {
        }

        public Language(string value)
        {
            this.Value = value;
        }

        private Language(SerializationInfo info, StreamingContext context)
        {
            this._value = info.GetString("_value");
        }

        public static Language Invariant
        {
            get
            {
                return CultureInfo.InvariantCulture;
            }
        }

        public int Length
        {
            get
            {
                int value = 0;

                if (!string.IsNullOrEmpty(this._value))
                {
                    value = this._value.Length;
                }

                return value;
            }
        }

        private string Value
        {
            get
            {
                return this._value;
            }

            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    this._value = value ?? string.Empty;
                }
                else
                {
                    if (Language.IsValid(value))
                    {
                        this._value = value;
                    }
                    else
                    {
                        throw new FormatException("Language value can only be in the format of en or en-GB.");
                    }
                }
            }
        }

        public static bool operator ==(Language obj, CultureInfo comparand)
        {
            return obj == (Language)comparand;
        }

        public static bool operator !=(Language obj, CultureInfo comparand)
        {
            return obj != (Language)comparand;
        }

        public static bool operator ==(Language obj, Language comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = obj.Equals(comparand);
            }

            return result;
        }

        public static bool operator !=(Language obj, Language comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = !object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = !obj.Equals(comparand);
            }

            return result;
        }

        public static bool operator <(Language obj, Language comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = !object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = 0 > obj.CompareTo(comparand);
            }

            return result;
        }

        public static bool operator >(Language obj, Language comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = false;
            }
            else
            {
                result = 0 < obj.CompareTo(comparand);
            }

            return result;
        }

        public static implicit operator CultureInfo(Language language)
        {
            return (null == language)
                ? null as CultureInfo
                : (string.IsNullOrEmpty(language) ? CultureInfo.InvariantCulture : new CultureInfo(language));
        }

        public static implicit operator Language(CultureInfo culture)
        {
            return (null == culture) ? null as Language : new Language(culture.Name);
        }

        public static implicit operator Language(string value)
        {
            return (null == value) ? null as Language : new Language(value);
        }

        public static implicit operator string(Language language)
        {
            return (null == language) ? null as string : language.Value;
        }

        public static bool IsValid(string value)
        {
            bool result = false;

            if (null == value)
            {
                result = false;
            }
            else if (0 == value.Length)
            {
                result = true;
            }
            else
            {
                result = _regex.IsMatch(value);
            }

            return result;
        }

        public int CompareTo(Language other)
        {
            int result = 0;

            if (null == other)
            {
                result = 1;
            }
            else
            {
                result = string.CompareOrdinal(this.Value, other.Value);
            }

            return result;
        }

        public int CompareTo(object obj)
        {
            int result = 0;

            if (null == obj)
            {
                result = 1;
            }
            else
            {
                Language cast = obj as Language;
                if (null == cast)
                {
                    throw new ArgumentOutOfRangeException("obj");
                }

                result = string.CompareOrdinal(this.Value, cast.Value);
            }

            return result;
        }

        public bool Equals(Language other)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, other))
            {
                if (object.ReferenceEquals(this, other))
                {
                    result = true;
                }
                else
                {
                    result = 0 == this.CompareTo(other);
                }
            }

            return result;
        }

        public override bool Equals(object obj)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, obj))
            {
                if (object.ReferenceEquals(this, obj))
                {
                    result = true;
                }
                else
                {
                    Language cast = obj as Language;

                    if (!object.ReferenceEquals(null, cast))
                    {
                        result = string.Equals(this.Value, cast.Value, StringComparison.OrdinalIgnoreCase);
                    }
                }
            }

            return result;
        }

        public override int GetHashCode()
        {
            return this.Value.GetHashCode();
        }

        public override string ToString()
        {
            return this.Value;
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (null == info)
            {
                throw new ArgumentNullException("info");
            }

            info.AddValue("_value", this._value);
        }
    }
}